﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DocumentService.cs" company="DST Nexdox">
//   Copyright (c) DST Nexdox. All rights reserved.
// </copyright>
// <summary>
//   Defines the DocumentService type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using ClientProxies;
using Common.Contracts;

namespace Services
{
  using System;
  using System.Collections.Generic;
  using System.Linq;

  using BusinessEngineInterfaces;
  using ClientProxies.ArchiveServiceReference;
  using Entities;
  using Exceptions;
  using ServiceInterfaces;
  using UnityRepository.Interfaces;

  public class DocumentService : IDocumentService
  {
    private readonly IDocumentRepository documentRepository;
    private readonly IDocTypeRepository docTypeRepositry;
    private readonly ISubDocTypeRepository subDocTypeRepositry;
    private readonly IManCoRepository manCoRepository;
    private readonly IHouseHoldRepository houseHoldRepository;
    private readonly IIndexNameCriteraService indexNameCriteraService;
    //private readonly IDocument archiveService;
    private IServiceFactory archiveServiceFactory;
    private readonly ISearchEngine documentEngine;
    private AuthenticationData authenticationData;

    // Constuctor required for NTGEN94
    public DocumentService(
      IDocumentRepository documentRepository,
      IIndexNameCriteraService indexNameCriteraService,
      IDocTypeRepository docTypeRepositry,
      ISubDocTypeRepository subDocTypeRepository,
      IManCoRepository manCoRepository,
      IHouseHoldRepository houseHoldRepository)
    {
      this.documentRepository = documentRepository;
      this.indexNameCriteraService = indexNameCriteraService;
      this.docTypeRepositry = docTypeRepositry;
      this.subDocTypeRepositry = subDocTypeRepository;
      this.manCoRepository = manCoRepository;
      this.houseHoldRepository = houseHoldRepository;
    }

    // Constuctor required for NTGEN99
    public DocumentService(
      IDocumentRepository documentRepository,
      IIndexNameCriteraService indexNameCriteraService,
      ISearchEngine documentEngine,
      IDocTypeRepository docTypeRepositry,
      ISubDocTypeRepository subDocTypeRepository,
      IManCoRepository manCoRepository,
      IHouseHoldRepository houseHoldRepository)
    {
      this.documentRepository = documentRepository;
      this.indexNameCriteraService = indexNameCriteraService;
      this.documentEngine = documentEngine;
      this.docTypeRepositry = docTypeRepositry;
      this.subDocTypeRepositry = subDocTypeRepository;
      this.manCoRepository = manCoRepository;
      this.houseHoldRepository = houseHoldRepository;
    }

    public DocumentService(
      IServiceFactory archiveServiceFactory,
      IDocumentRepository documentRepository,
      IIndexNameCriteraService indexNameCriteraService,
      ISearchEngine documentEngine,
      IDocTypeRepository docTypeRepositry,
      ISubDocTypeRepository subDocTypeRepository,
      IManCoRepository manCoRepository,
      IHouseHoldRepository houseHoldRepository)
    {
        this.archiveServiceFactory = archiveServiceFactory;
      this.documentRepository = documentRepository;
      this.indexNameCriteraService = indexNameCriteraService;
      this.documentEngine = documentEngine;
      this.docTypeRepositry = docTypeRepositry;
      this.subDocTypeRepositry = subDocTypeRepository;
      this.manCoRepository = manCoRepository;
      this.houseHoldRepository = houseHoldRepository;
    }

    public PagedResult<IndexedDocumentData> GetDocuments(Int32 pageNumber, Int32 itemsPerPage, String grid, Boolean filterHouseHeld = false, Boolean filterUnapproved = false, Boolean filterApproved = false)
    {
      try
      {
        Int32 startRow;
        Int32 endRow;
        this.documentEngine.GetStartEndRow(pageNumber, itemsPerPage, out startRow, out endRow);

        this.GetAuthData();

        Boolean filterApplied = filterHouseHeld || filterUnapproved || filterApproved;

        DocumentSearchResultsData data;
        if (filterApplied)
        {
          IDocument archiveServiceProxy = archiveServiceFactory.CreateDocumentServiceClient<IDocument>();

          data = archiveServiceProxy.DocumentSearch(
            this.authenticationData,
            1,
            10000,
            String.Empty,
            grid,
            1,
            "*",
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty);
        }
        else
        {
            IDocument archiveServiceProxy = archiveServiceFactory.CreateDocumentServiceClient<IDocument>();
            data = archiveServiceProxy.DocumentSearch(
            this.authenticationData,
            startRow,
            endRow,
            String.Empty,
            grid,
            1,
            "*",
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty,
            String.Empty);
        }

        if (filterApplied)
        {
          this.ApplyFilters(grid, filterHouseHeld, filterUnapproved, filterApproved, data);
        }

        return this.documentEngine.GetPagedResults(startRow, endRow, pageNumber, itemsPerPage, data, filterApplied);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to search for documents", e);
      }
    }

    public PagedResult<IndexedDocumentData> GetDocuments(
      Int32 pageNumber,
      Int32 itemsPerPage,
      String docType,
      String subDocType,
      String addresseeSubType,
      String accountNumber,
      String mailingName,
      IList<String> managementCompanies,
      String investorReference,
      DateTime? processingDateFrom,
      DateTime? processingDateTo,
      String primaryHolder,
      String agentReference,
      String addresseePostCode,
      String emailAddress,
      String faxNumber,
      DateTime? contractDate,
      DateTime? paymentDate,
      String documentNumber)
    {
      try
      {
        Int32 startRow;
        Int32 endRow;
        this.documentEngine.GetStartEndRow(pageNumber, itemsPerPage, out startRow, out endRow);

        this.GetAuthData();

        var searchCriteria = this.indexNameCriteraService.BuildSearchCriteria(
          docType,
          subDocType,
          addresseeSubType,
          accountNumber,
          mailingName,
          managementCompanies,
          investorReference,
          processingDateFrom,
          processingDateTo,
          primaryHolder,
          agentReference,
          addresseePostCode,
          emailAddress,
          faxNumber,
          contractDate,
          paymentDate,
          documentNumber);

        DateTime fromDate = processingDateFrom ?? new DateTime(2000, 1, 1);
        DateTime toDate = processingDateTo == null ? DateTime.Now : processingDateTo.Value.AddHours(23).AddMinutes(59).AddSeconds(59);

        IDocument archiveServiceProxy = archiveServiceFactory.CreateDocumentServiceClient<IDocument>();
        var data = archiveServiceProxy.DocumentSearchMapVerbose(this.authenticationData, startRow, endRow, 1, fromDate, toDate, searchCriteria);

        return this.documentEngine.GetPagedResults(startRow, endRow, pageNumber, itemsPerPage, data);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to search for documents", e);
      }
    }

    public Byte[] GetDocumentStream(String documentId)
    {
      try
      {
        this.GetAuthData();
        IDocument archiveServiceProxy = archiveServiceFactory.CreateDocumentServiceClient<IDocument>();
        return archiveServiceProxy.GetDocument(this.authenticationData, documentId);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve document", e);
      }
    }

    public Document GetDocument(String documentId)
    {
      try
      {
        return this.documentRepository.GetDocument(documentId);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve document", e);
      }
    }

    public IEnumerable<Document> GetDocuments(IEnumerable<String> documentIds)
    {
      try
      {
        return this.documentRepository.GetDocuments(documentIds);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve document set", e);
      }
    }

    public IEnumerable<Document> GetDocuments(IEnumerable<Int32> ids)
    {
      try
      {
        return this.documentRepository.GetDocuments(ids);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve documents", e);
      }
    }

    public void AddDocument(String documentId, Int32 docTypeId, Int32 subDocTypeId, Int32 manCoId, Int32? gridRunId, String mailPrintFlag)
    {
      try
      {
        var document = new Document(mailPrintFlag)
                         {
                           DocumentId = documentId,
                           DocTypeId = docTypeId,
                           SubDocTypeId = subDocTypeId,
                           ManCoId = manCoId,
                           GridRunId = gridRunId
                         };

        this.documentRepository.Create(document);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to add document", e);
      }
    }

    public void AddDocument(String documentId, String docTypeCode, String subDocTypeCode, String manCoCode, Int32? gridRunId)
    {
      try
      {
        var docType = this.docTypeRepositry.GetDocType(docTypeCode);
        var subDocType = this.subDocTypeRepositry.GetSubDocType(subDocTypeCode);
        var manCo = this.manCoRepository.GetManCo(manCoCode);
        this.AddDocument(documentId, docType.Id, subDocType.Id, manCo.Id, gridRunId, null);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to add document", e);
      }
    }

    public IList<Document> GetDocuments(String grid)
    {
      try
      {
        return this.documentRepository.GetDocuments(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve documents", e);
      }
    }

    public Int32 GetDocumentCount(String grid)
    {
      try
      {
        return this.documentRepository.GetDocumentsCount(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve document count", e);
      }
    }

    public IList<Document> GetDocumentsWithApprovalAndRejectionEntities(String grid)
    {
      try
      {
        return this.documentRepository.GetDocumentsWithApprovalAndRejectionEntities(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve documents for grid", e);
      }
    }

    public IList<Document> GetDocumentsWithApprovalRejectionAndHouseHoldEntities(String grid)
    {
      try
      {
        return this.documentRepository.GetDocumentsIncludingApprovalRejectionAndHouseHoldEntities(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve documents for grid", e);
      }
    }

    public IList<Document> GetUnApprovedDocuments(String grid)
    {
      try
      {
        return this.documentRepository.GetUnApprovedDocuments(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve unapproved documents", e);
      }
    }

    public IList<Document> GetApprovedDocuments(String grid)
    {
      try
      {
        return this.documentRepository.GetApprovedDocuments(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve approved documents", e);
      }
    }

    public Int32 GetApprovedDocumentsCount(String grid)
    {
      try
      {
        return this.documentRepository.GetApprovedDocumentsCount(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve approved documents count", e);
      }
    }

    public Int32 GetUnApprovedDocumentsCount(String grid)
    {
      try
      {
        return this.documentRepository.GetUnApprovedDocumentsCount(grid);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve unapproved documents count", e);
      }
    }

    public IList<KpiReportData> GetDocuments(Int32 mancoId, DateTime startDate, DateTime endDate)
    {
      try
      {
        return this.documentRepository.GetDocuments(mancoId, startDate, endDate);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve documents", e);
      }
    }

    public IList<Document> GetDocumentsForBasket(String cartId)
    {
      try
      {
        return this.documentRepository.GetDocumentsForBasket(cartId);
      }
      catch (Exception e)
      {
        throw new UnityException(String.Format("Unable to retrieve documents for cart '{0}'", cartId), e);
      }
    }

    public IList<Document> GetApprovedAndNotExported()
    {
      try
      {
        return this.documentRepository.GetApprovedAndNotExported();
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to retrieve approved and not exported documents", e);
      }
    }

    public void UpdateAsHouseheld(Int32 documentId, Int32 houseHoldingRunId)
    {
      try
      {
        this.UpdateAsHouseheld(new List<Int32> { documentId }, houseHoldingRunId);
      }
      catch (Exception e)
      {
        throw new UnityException("Unable to update document", e);
      }
    }

    public void UpdateAsHouseheld(IEnumerable<Int32> docIds, Int32 houseHoldingRunId)
    {
      try
      {
        List<HouseHold> houseHolds = docIds.Select(docId =>
          new HouseHold
          {
            HouseHoldingRunId = houseHoldingRunId,
            HouseHoldDate = DateTime.Now,
            DocumentId = docId
          }).ToList();

        this.houseHoldRepository.Create(houseHolds);
      }
      catch (Exception e)
      {
        throw new UnityException("Error updating documents as househeld", e);
      }
    }

    private void GetAuthData()
    {
      this.authenticationData = new AuthenticationData
      {
        Source = "Unity",
        Destination = "NT-Archive",
        UserName = "paul",
        PassToken = "9F33A7C798AF6FD6ABB28049D9C1B3EDFA2FD24A",
        RequestId = "1",
        CurrentDateTime = DateTime.Now,
        OriginatingUser = "unity"
      };
    }

    private void ApplyFilters(
      String grid,
      Boolean filterHouseHeld,
      Boolean filterUnapproved,
      Boolean filterApproved,
      DocumentSearchResultsData data)
    {
      IList<Document> documents = this.GetDocumentsWithApprovalRejectionAndHouseHoldEntities(grid);
      Dictionary<String, Document> indexedDocuments = documents.ToDictionary(document => document.DocumentId);

      if (filterHouseHeld)
      {
        this.FilterHouseHolding(data, indexedDocuments);
      }

      if (filterUnapproved)
      {
        this.FilterUnapproved(data, indexedDocuments);
      }

      if (filterApproved)
      {
        this.FilterApprovedAndRejected(data, indexedDocuments);
      }
    }

    private void FilterHouseHolding(DocumentSearchResultsData data, Dictionary<String, Document> indexedDocuments)
    {
      for (Int32 i = data.DocumentList.Count - 1; i >= 0; i--)
      {
        String documentId = data.DocumentList[i].Id.ToString();
        Document document = indexedDocuments[documentId];
        if (document.HouseHold == null)
        {
          data.DocumentList.RemoveAt(i);
        }
      }
    }

    private void FilterUnapproved(DocumentSearchResultsData data, Dictionary<String, Document> indexedDocuments)
    {
      for (Int32 i = data.DocumentList.Count - 1; i >= 0; i--)
      {
        String documentId = data.DocumentList[i].Id.ToString();
        Document document = indexedDocuments[documentId];

        if (document.Approval == null)
        {
          data.DocumentList.RemoveAt(i);
        }
      }
    }

    private void FilterApprovedAndRejected(DocumentSearchResultsData data, Dictionary<String, Document> indexedDocuments)
    {
      for (Int32 i = data.DocumentList.Count - 1; i >= 0; i--)
      {
        String documentId = data.DocumentList[i].Id.ToString();
        Document document = indexedDocuments[documentId];

        if (document.Approval != null || document.Rejection != null)
        {
          data.DocumentList.RemoveAt(i);
        }
      }
    }
  }
}
